﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;
using LexLib.LexemNameSpace;
using System.Threading;

namespace LexLib.LexerNameSpace
{
    public class Lexer
    {
        private List<Lexem> arrOfTokens = new List<Lexem>();//Список лексем, который и должен вернуть лексер
        private LexerTree lexTree = new LexerTree();//по-сути список ключевых слов
        private bool isChecked = false;//уже забыл)))
        private string text;//текст, предаваемый лексеру

        public List<Lexem> ArrOfLexems
        {
            get { return arrOfTokens; }
        }

        /// <summary>
        /// Метод получает какую-то строку, котороую пытается разбить на лексемы. 
        /// Потом будет: Возвращает ArrayList лексем 
        /// </summary>
        /// <param name="text"></param>
        public void automat(string myText)
        {
            isChecked = false;
            arrOfTokens.Clear();
            text = myText;
            automat(0);
        }

        /// <summary>
        /// Собственно автомат
        /// </summary>
        /// <param name="i"></param>
        private void automat(int i)
        {
            bool flag = false;//совпадает ли хотя бы частично с каким-нибудь ключевым словом
            //i - текущая позиция
            if (i < text.Length)
            {
                //убирание пробелов,переходов сторк и табуляций
                while ((i < text.Length) && ((text[i] == ' ') || (text[i] == '\n') || (text[i] == '\t')))
                {
                    i++;
                }
                int j = i;
                //Проверка на комментарий
                if (i < text.Length)
                {
                    if ((text[i] == '/') && (!isChecked))
                    {
                        isChecked = true;
                        if (i < text.Length - 1)
                        {
                            if ((text[i + 1] == '/'))
                            {
                                while ((i < text.Length) && (text[i] != '\n'))
                                {
                                    i++;
                                }
                                arrOfTokens.Add(new Lexem(new Comment(), j, i));
                                automat(i + 1);
                            }
                            else if (text[i + 1] == '*')
                            {
                                while ((i < text.Length - 1) && ((text[i] != '*') || (text[i + 1] != '/')))
                                {
                                    i++;
                                }
                                arrOfTokens.Add(new Lexem(new Comment(), j, i+1));
                                automat(i + 2);
                            }
                            else
                            {
                                automat(i);
                            }
                        }
                        else
                        {
                            //i = text.Length;
                            automat(i);
                        }

                    }
                    //проверка на константную строку
                    else if ((text[i] == '\"'))
                    {
                        i++;
                        while ((i < text.Length) && (text[i] != '\n') && (text[i] != '\"'))
                        {
                            i++;
                        }
                        if ((i < text.Length) && (text[i] == '\"'))
                        {
                            arrOfTokens.Add(new Lexem(new ConstString(), j, i));
                            automat(i + 1);
                        }
                        else
                        {
                            arrOfTokens.Add(new Lexem(new Error(), j, i));
                            automat(i + 1);
                        }
                    }
                    else if (text[i] == '\'')
                    {
                        i++;
                        if ((i < text.Length - 1) && (text[i + 1] == '\''))
                        {
                            arrOfTokens.Add(new Lexem(new ConstChar(), j, i + 1));
                            automat(i + 2);
                        }
                        else
                        {
                            arrOfTokens.Add(new Lexem(new Error(), j, i));
                            automat(i + 1);
                        }
                    }
                    else
                    {
                        isChecked = false;
                        //проверка на наличие в списке ключевых слов
                        while ((i < text.Length) && (lexTree.IsLetterExist(text[i])))
                        {
                            flag = true;
                            i++;
                        }
                        if (lexTree.IsEnd())
                        {
                            if ((lexTree.GetLexemType.RequireBlank) && ((i >= text.Length) || (!((char.IsSymbol(text[i])) || (text[i] == '(') || (text[i] == ')') || (text[i] == '{') || (text[i] == '}') || (text[i] == '\"') || (text[i] == ';')))))
                            {
                                if ((i >= text.Length) || (text[i] == ' ') || (text[i] == '\n') || (text[i] == '\t'))
                                {
                                    AddToLexemArray(j, i, true);
                                    lexTree.Dispose();
                                    automat(i + 1);
                                }
                                else
                                {
                                    i = getToTheEndOfWord(i, j, lexTree.CurWord);
                                    lexTree.Dispose();
                                    automat(i);
                                    /*no token*/
                                }
                            }
                            else
                            {
                                AddToLexemArray(j, i, true);
                                lexTree.Dispose();
                                automat(i);
                            }
                        }
                        else
                        {
                            if (flag)
                            {
                                i = getToTheEndOfWord(i - 1, j, lexTree.CurWord);
                            }
                            else
                            {
                                i = getToTheEndOfWord(i, j, lexTree.CurWord);
                            }
                            lexTree.Dispose();
                            automat(i);
                            /*no token*/
                        }
                    }
                }
            }
            ;
        }

        /// <summary>
        /// Метод используемый для того, чтобы дойти до конца слова, которое не является ключевым токеном
        /// Принимает - весь текст, текущую позициую и неполное слово(или пустое слово)
        /// возвращает номер символа стоящего после конца слова (скорее всего номер символа пробела)
        /// </summary>
        private int getToTheEndOfWord(int curIndex,int beginIndex,string token)
        {
            bool flag = true;
            while ((curIndex < text.Length) && (((char.IsPunctuation(text[curIndex])) 
                                                    && (text[curIndex] != '(') 
                                                    && (text[curIndex] != ';') 
                                                    && (text[curIndex] != ')') 
                                                    && (text[curIndex] != '{') 
                                                    && (text[curIndex] != '}') 
                                                    && (text[curIndex] != '\"') 
                                                    && (text[curIndex] != '-') 
                                                    && (text[curIndex] != '/')
                                                    && (text[curIndex] != '*')) 
                                                || (char.IsLetterOrDigit(text[curIndex]))
                                                || (text[curIndex]== '`')))
            {
                if ((!char.IsDigit(text[curIndex])))
                {
                    flag = false;
                }
                //проверка на начало коммента - он может быть не отделен пробелом или чем-то другим 
                if ((curIndex < text.Length - 1) && (text[curIndex] == '/') && ((text[curIndex + 1] == '/') || (text[curIndex + 1] == '*')))
                {
                    break;
                }
                token += text[curIndex];
                curIndex++; 
            }
            if (flag)
            {
                arrOfTokens.Add(new Lexem(new IntNumberType(), beginIndex, curIndex - 1));
            }
            else
            {
                AddToLexemArray(beginIndex, curIndex - 1, false);
            }
            return curIndex;
        }

        /// <summary>
        /// Добавление лексемы в список лексем
        /// </summary>
        /// <param name="beginIndex">Начальный индекс</param>
        /// <param name="endIndex">Конечный индекс</param>
        /// <param name="isKeyWord">Является ли ключевым словом</param>
        private void AddToLexemArray(int beginIndex, int endIndex, bool isKeyWord)
        {
            endIndex++;
            ILexemType curLexemType;
            if (!isKeyWord)
            {
                curLexemType = new VarNameType(text.Substring(beginIndex,(endIndex-beginIndex)));//см ниже
            }
            else
            {
                try
                {
                    curLexemType = lexTree.GetLexemType;
                }
                catch (NullReferenceException)
                {
                    curLexemType = new VarNameType(text.Substring(beginIndex, (endIndex - beginIndex)));//тут будет не otherkeyword, а тип переменной 
                }
            }
            arrOfTokens.Add(new Lexem(curLexemType, beginIndex, endIndex));
        }

        /// <summary>
        /// Добавление новых ключевых слов
        /// </summary>
        /// <param name="str">Новое ключевое слово</param>
        /// <param name="isVar">Роль ключевого слова</param>
        /// <param name="cl">Цвет дополнительного слова</param>
        public void AddKeyWord(string str, bool isVar, Color cl)
        {
            switch (str) 
            {
                case "bool":
                    {
                        lexTree.AddKeyWord(str, new BoolType());
                        break;
                    }
                case "true":
                case "false":
                    {
                        lexTree.AddKeyWord(str, new ConstBool());
                        break;
                    }
                case "string":
                    {
                        lexTree.AddKeyWord(str, new StringType());
                        break;
                    }
                case "char":
                    {
                        lexTree.AddKeyWord(str, new CharType());
                        break;
                    }
                case "int":
                    {
                        lexTree.AddKeyWord(str, new IntType());
                        break;
                    }
                case "double":
                    {
                        lexTree.AddKeyWord(str, new DoubleType());
                        break;
                    }
                case "for":
                    {
                        lexTree.AddKeyWord(str, new For());
                        break;
                    }
                case "while":
                    {
                        lexTree.AddKeyWord(str, new While());
                        break;
                    }
                case "if":
                    {
                        lexTree.AddKeyWord(str, new If());
                        break;
                    }
                case "else":
                    {
                        lexTree.AddKeyWord(str, new Else());
                        break;
                    }
                case "program":
                    {
                        lexTree.AddKeyWord(str, new ProgramType());
                        break;
                    }
                case "function":
                    {
                        lexTree.AddKeyWord(str, new Function());
                        break;
                    }
                case "return":
                    {
                        lexTree.AddKeyWord(str, new Return());
                        break;
                    }
                case "+":
                case "-":
                case "*":
                case "/":
                    {
                        lexTree.AddKeyWord(str, new SyntaxType());
                        break;
                    }
                case "++":
                case "--":
                case "+=":
                case "-=":
                case "*=":
                case "/=":
                case "=":
                    {
                        lexTree.AddKeyWord(str, new Modificator());
                        break;
                    }
                case ">":
                case "<":
                case "==":
                case "!=":
                case ">=":
                case "<=":
                    {
                        lexTree.AddKeyWord(str, new ConditionType());
                        break;
                    }
                case "(":
                    {
                        lexTree.AddKeyWord(str, new OpenNormalBracketType());
                        break;
                    }
                case ")":
                    {
                        lexTree.AddKeyWord(str, new CloseNormalBracketType());
                        break;
                    }
                case "{":
                    {
                        lexTree.AddKeyWord(str, new OpenFigureBracketType());
                        break;
                    }
                case "}":
                    {
                        lexTree.AddKeyWord(str, new CloseFigureBracketType());
                        break;
                    }
                case ";":
                    {
                        lexTree.AddKeyWord(str, new EndOfLineType());
                        break;
                    }
                /*default:
                    {
                        lexTree.AddKeyWord(str, new OtherKeyWord(isVar, cl));
                        break;
                    }*/
            }
        }


        public void RemoveKeyWord(string str)
        {
            lexTree.DeleteKeyWord(str);
        }

        public Dictionary<string,ILexemType> GetKeyWords()
        {
            return lexTree.GetAllKeyWords();
        }

        public void Colorization(bool col, string str)
        {
            lexTree.SetColorization(str, col);
        }
    }
}
